package com.atguigu.gmall.activity.receiver;

import com.atguigu.gmall.activity.model.SeckillGoods;
import com.atguigu.gmall.activity.model.UserRecode;
import com.atguigu.gmall.activity.service.SeckillGoodsService;
import com.atguigu.gmall.common.constant.MqConst;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.util.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.rabbitmq.client.Channel;
import io.netty.handler.traffic.GlobalTrafficShapingHandler;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.BoundListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

/**
 * @author: atguigu
 * @create: 2023-03-17 10:38
 */
@Slf4j
@Component
public class SeckillReceiver {


    @Autowired
    private SeckillGoodsService seckillGoodsService;

    @Autowired
    private RedisTemplate redisTemplate;

    @RabbitListener(bindings = @QueueBinding(
            exchange = @Exchange(MqConst.EXCHANGE_DIRECT_TASK),
            value = @Queue(value = MqConst.QUEUE_TASK_1, durable = "true"),
            key = {MqConst.ROUTING_TASK_1}
    ))
    public void processImportSeckill2Redis(Message message, Channel channel) {
        try {
            log.info("接收到商品预热消息");
            //1.查询数据库中当日参与秒杀商品 审核状态:通过 秒杀商品库存大于0
            LambdaQueryWrapper<SeckillGoods> queryWrapper = new LambdaQueryWrapper<>();
            //审核状态取值 1:审核通知  2:审核不通过
            queryWrapper.eq(SeckillGoods::getStatus, "1");
            queryWrapper.gt(SeckillGoods::getStockCount, 0);
            //在SQL后增加函数 采用 apply方法进行拼接SQL
            queryWrapper.apply("DATE_FORMAT(start_time,'%Y-%m-%d') = '" + DateUtil.formatDate(new Date()) + "'");
            List<SeckillGoods> seckillGoodsList = seckillGoodsService.list(queryWrapper);
            //2.将参与秒杀商品加入分布式缓存Redis
            if (!CollectionUtils.isEmpty(seckillGoodsList)) {
                //2.1 将所有参与秒杀商品业务数据放入缓存:Hash类型
                //2.1.1 构建所有秒杀商品业务数据Hash结构key
                String seckillGoodsKey = RedisConst.SECKILL_GOODS;
                //2.12.2 构建操作秒杀商品hash操作对象
                BoundHashOperations<String, String, SeckillGoods> hashOps = redisTemplate.boundHashOps(seckillGoodsKey);
                for (SeckillGoods seckillGoods : seckillGoodsList) {
                    //2.1.3 判断秒杀hash中是否包含秒杀商品
                    if (hashOps.hasKey(seckillGoods.getSkuId().toString())) {
                        continue;
                    }
                    hashOps.put(seckillGoods.getSkuId().toString(), seckillGoods);

                    //2.2 每件秒杀商品都有库存队列:List类型
                    //2.2.1 构建当前秒杀商品库存Key seckill:stock:商品SkuId
                    String skuStockKey = RedisConst.SECKILL_STOCK_PREFIX + seckillGoods.getSkuId();
                    //2.2.2 根据商品秒杀库存数量生成库存队列
                    BoundListOperations<String, String> stockListOps = redisTemplate.boundListOps(skuStockKey);
                    for (Integer i = 0; i < seckillGoods.getStockCount(); i++) {
                        stockListOps.leftPush(seckillGoods.getSkuId().toString());
                    }

                    //3.将商品"状态位"加入到本地缓存中
                    //将商品状态 发送到Redis 发布订阅话题:seckillpush
                    String msg = seckillGoods.getSkuId() + ":1";
                    redisTemplate.convertAndSend("seckillpush", msg);
                }
            }
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (IOException e) {
            e.printStackTrace();
            log.error("[秒杀服务]秒杀商品预热异常:{}", e);
        }

    }


    /**
     * 处理秒杀队列中秒杀请求
     *
     * @param userRecode
     * @param message
     * @param channel
     */
    @SneakyThrows
    @RabbitListener(bindings = @QueueBinding(
            exchange = @Exchange(MqConst.EXCHANGE_DIRECT_SECKILL_USER),
            value = @Queue(value = MqConst.QUEUE_SECKILL_USER, durable = "true"),
            key = MqConst.ROUTING_SECKILL_USER
    ))
    public void processSeckillRequest(UserRecode userRecode, Message message, Channel channel) {
        try {
            if (userRecode != null) {
                log.info("[秒杀服务]监听到秒杀请求消息:{}", userRecode);
                seckillGoodsService.processSeckillRequest(userRecode);
            }
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (IOException e) {
            e.printStackTrace();
            log.error("[秒杀服务]处理秒杀请求异常:{}", e);
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
        }
    }


    /**
     * 监听秒杀成功后扣减库存逻辑
     *
     * @param skuId   秒杀商品ID
     * @param message
     * @param channel
     */
    @SneakyThrows
    @RabbitListener(bindings = @QueueBinding(
            exchange = @Exchange(MqConst.EXCHANGE_DIRECT_SECKILL_STOCK),
            value = @Queue(value = MqConst.QUEUE_SECKILL_STOCK, durable = "true"),
            key = MqConst.ROUTING_SECKILL_STOCK
    ))
    public void processDeductStock(Long skuId, Message message, Channel channel) {
        try {
            if (skuId != null) {
                log.info("[秒杀服务]监听秒杀成功扣减库存消息:{}", skuId);
                seckillGoodsService.deductStock(skuId);
            }
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (IOException e) {
            e.printStackTrace();
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
        }
    }


    /**
     * todo:处理恢复秒杀商品库存监听器 接收到要恢复商品消息userRecord
     * 1.查询用户是否保存秒杀订单  用户秒杀订单 hashseckill:orders:users userId+":"+skuId orderId
     * 2.远程调用订单微服务获取订单支付状态 如果订单未支付:
     *    *发送关闭订单消息将订单关闭
     *    *恢复 秒杀商品库存  listseckill:stock:skuId
     *    *恢复所有秒杀商品 hashseckill:goods   skuId  商品信息(库存数量)
     */


    /**
     * 定时清理秒杀当日产生缓存+数据库
     *
     * @param message
     * @param channel
     */
    @SneakyThrows
    @RabbitListener(bindings = @QueueBinding(
            exchange = @Exchange(MqConst.EXCHANGE_DIRECT_TASK),
            value = @Queue(value = MqConst.QUEUE_TASK_18, durable = "true"),
            key = MqConst.ROUTING_TASK_18
    ))
    public void processCleanCache(Message message, Channel channel) {
        try {
            log.info("[秒杀服务] 清理秒杀当日产生缓存");
            seckillGoodsService.processCleanCache();
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (IOException e) {
            e.printStackTrace();
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
        }
    }
}
